/**
 *
 */
package platform.common.dao.impl;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.hibernate.LockMode;
import org.hibernate.Query;

import platform.common.bo.AbstractNestedSetTreeNode;
import platform.common.dao.IDomainProvider;
import platform.common.dao.INestSetTreeDao;


import platform.common.dao.impl.DaoBaseImpl;
import platform.common.dao.impl.DaoException;

/**
 * @author daizs
 *
 */
public abstract class NestSetTreeDaoImpl extends DaoBaseImpl implements INestSetTreeDao {

	/**
	 * 更新一个嵌套集合树节点，不改变节点在树的位置，只更新内容，即不改变节点的左右值
	 *
	 * @param treeNode 嵌套集合树节点
	 * @throws DaoException
	 */
	public void updateTreeNode(AbstractNestedSetTreeNode treeNode)
			throws DaoException {
		//Transaction tx = null;
		//tx = this.getSession().beginTransaction();
		try {
			if (treeNode == null) {
				throw new DaoException("不能保存一个空的树节点");
			}
			AbstractNestedSetTreeNode nodeInDB = (AbstractNestedSetTreeNode) this
					.getSession().get(treeNode.getClass(), treeNode.getId(),
							LockMode.UPGRADE);
			if (nodeInDB == null) {
				throw new DaoException("不能在数据库中找到ID为" + treeNode.getId()
						+ "的树节点!无法保存!");
			}
			if (!(nodeInDB.getLeftValue() != treeNode.getLeftValue()
					|| nodeInDB.getRightValue() != treeNode.getRightValue()
					|| (treeNode.getIsolatedFlag() != null && nodeInDB
							.getIsolatedFlag().toString().equals(
									treeNode.getIsolatedFlag())))) {
				throw new DaoException("前序遍历树不允许自行修改节点的左右值或者隔离标识");
			}
			
			this.getSession().saveOrUpdate(treeNode);
			//tx.commit();
		} catch (Exception e) {
			//tx.rollback();
			throw this.handleDaoException(e);
		}
	}
	
	/**
	 * 插入一个嵌套集合树子节点
	 *
	 * @param treeNode 嵌套集合树节点
	 * @param parentId 父节点ID
	 * @throws DaoException
	 */
	public Serializable insertTreeNode(AbstractNestedSetTreeNode treeNode,
			Serializable parentId) throws DaoException {

		//Transaction tx = null;
		//tx = this.getSession().beginTransaction();
		try {
			Class clsTreeNode = treeNode.getClass();
			log.debug("BaseDao 开始插入一个 treeNode ");
			if (parentId == null) {// 第一层
				String maxRgtHql = "select max(rightValue) from "
						+ clsTreeNode.getName() + " where treeLevel=0 and " +
						"isolatedFlag='"+treeNode.getIsolatedFlag()+"'";// 查询第一层最大的右值
				int maxRgt = 0;
				Integer maxRgtInteger = (Integer) this.getSession()
						.createQuery(maxRgtHql).uniqueResult();
				if (maxRgtInteger != null) {
					maxRgt = maxRgtInteger.intValue();
				}
				treeNode.setLeftValue(maxRgt + 1);
				treeNode.setRightValue(maxRgt + 2);
				return this.getSession().save(treeNode);
			} else {
				this.getSession().createQuery(
						"from " + clsTreeNode.getName() + " treeO")
						.setLockMode("treeO", LockMode.UPGRADE);
				AbstractNestedSetTreeNode parent = (AbstractNestedSetTreeNode) this
						.getSession().get(clsTreeNode, parentId);
				if (parent == null) {
					throw new DaoException("不能在数据库中找到id为" + parentId + "的父节点");
				}

				String maxRgtHql = "select max(rightValue) from "
						+ clsTreeNode.getName() + " where treeLevel="
						+ (parent.getTreeLevel() + 1) + " and leftValue>"
						+ parent.getLeftValue() + " and rightValue<"
						+ parent.getRightValue() + " and isolatedFlag = '"
						+ parent.getIsolatedFlag() + "'";// 查询父节点下一层最大的右值
				int maxRgt = 0;
				Integer maxRgtInteger = (Integer) this.getSession()
						.createQuery(maxRgtHql).uniqueResult();
				if (maxRgtInteger != null) {
					maxRgt = maxRgtInteger.intValue();
				}

				if (maxRgt > 0) {// 找到了兄弟节点
					maxRgt++;
					treeNode.setLeftValue(maxRgt);
					treeNode.setRightValue(maxRgt + 1);
					treeNode.setTreeLevel(parent.getTreeLevel() + 1);
				} else {// 没有找到兄弟节点，取父节点的左值加一作为左值
					maxRgt = parent.getLeftValue() + 1;
					treeNode.setLeftValue(parent.getLeftValue() + 1);
					treeNode.setRightValue(parent.getLeftValue() + 2);
					treeNode.setTreeLevel(parent.getTreeLevel() + 1);
				}
				String updHqlRgt = "UPDATE "
						+ clsTreeNode.getName()
						+ " SET rightValue = rightValue+2 WHERE rightValue >= :pRgt "
						+ "and isolatedFlag = '" + parent.getIsolatedFlag()
						+ "'";
				String updHqlLft = "UPDATE "
						+ clsTreeNode.getName()
						+ " SET leftValue = leftValue+2 WHERE leftValue >= :pRgt "
						+ "and isolatedFlag = '" + parent.getIsolatedFlag()
						+ "'";

				this.getSession().createQuery(updHqlRgt).setInteger("pRgt",
						maxRgt).executeUpdate();
				this.getSession().createQuery(updHqlLft).setInteger("pRgt",
						maxRgt).executeUpdate();
				treeNode.setIsolatedFlag(parent.getIsolatedFlag());
				return this.getSession().save(treeNode);
			}
			//tx.commit();
		} catch (Exception e) {
			//tx.rollback();
			throw this.handleDaoException(e);
		}
	}
	
	
	/**
	 * 
	 * <br/>方法描述:插入一个嵌套集合树节点,不需要构造左右值
	 * @param treeNode
	 * @throws DaoException
	 */
	public void insertTreeNodeNotSetLR(AbstractNestedSetTreeNode treeNode) throws DaoException{
		try {
			this.getSession().save(treeNode);
		} catch (Exception e) {
			throw this.handleDaoException(e);
		}
	}

	/**
	 * 插入一个嵌套集合树兄弟节点
	 *
	 * @param treeNode 嵌套集合树节点
	 * @param brotherId 兄弟节点
	 * @throws DaoException
	 */
	public void insertTreeNodeBrother(AbstractNestedSetTreeNode treeNode,
			Serializable brotherId) throws DaoException {

		//Transaction tx = null;
		//tx = this.getSession().beginTransaction();
		try {
			Class clsTreeNode = treeNode.getClass();
			log.debug("BaseDao 开始插入一个 treeNode ");
			this.getSession().createQuery(
					"from " + clsTreeNode.getName() + " treeO").setLockMode(
					"treeO", LockMode.UPGRADE);

			AbstractNestedSetTreeNode brother = (AbstractNestedSetTreeNode) this
					.getSession().get(clsTreeNode, brotherId);
			if (brother == null) {
				throw new DaoException("不能在数据库中找到id为" + brotherId + "的节点");
			}
			int maxRgt = brother.getLeftValue();// 获取兄弟节点的左值(插入节点的左值)
			treeNode.setLeftValue(brother.getLeftValue());// 插入节点的左值为插入对象的左值
			treeNode.setRightValue(brother.getLeftValue() + 1);// 插入节点的左值+1为插入对象的左值
			treeNode.setTreeLevel(brother.getTreeLevel());// 设置节点层次(插入节点的层次)
			// 更新插入点后的所有节点左值
			String updHqlRgt = "UPDATE "
					+ clsTreeNode.getName()
					+ " SET rightValue = rightValue+2 WHERE rightValue >= :pRgt "
					+ "and isolatedFlag = '" + brother.getIsolatedFlag() + "'";
			// 更新插入点后的所有节点右值
			String updHqlLft = "UPDATE " + clsTreeNode.getName()
					+ " SET leftValue = leftValue+2 WHERE leftValue >= :pRgt "
					+ "and isolatedFlag = '" + brother.getIsolatedFlag() + "'";

			this.getSession().createQuery(updHqlRgt).setInteger("pRgt", maxRgt)
					.executeUpdate();
			this.getSession().createQuery(updHqlLft).setInteger("pRgt", maxRgt)
					.executeUpdate();
			// 设置隔离标识
			treeNode.setIsolatedFlag(brother.getIsolatedFlag());
			this.getSession().save(treeNode);

			//tx.commit();
		} catch (Exception e) {
			//tx.rollback();
			throw this.handleDaoException(e);
		}
	}

	/**
	 *
	 * <br/>方法描述:某个节点在本层节点间上移
	 *
	 * @param strutId
	 *            上移的节点id
	 * @throws DaoException
	 */
	public void moveUp(AbstractNestedSetTreeNode treeNode) throws DaoException {
		//Transaction tx = null;
		//tx = this.getSession().beginTransaction();
		try {
			Class clsTreeNode = treeNode.getClass();
			log.debug("BaseDao 开始上移一个 treeNode ");
			

			this.getSession().createQuery(
					"from " + clsTreeNode.getName() + " treeO").setLockMode(
					"treeO", LockMode.UPGRADE);

			// 获取哥哥节点
			AbstractNestedSetTreeNode brother = null;

			String queryBrother = " from " + clsTreeNode.getName()
					+ " where rightValue=" + (treeNode.getLeftValue() - 1)
					+ " and isolatedFlag='" + treeNode.getIsolatedFlag() + "'";
			List list = this.getSession().createQuery(queryBrother).list();
			if (list.size() > 0) {
				brother = (AbstractNestedSetTreeNode) list.get(0);
			}
			if (brother == null) {
				return;
				// throw new DaoException("不能在数据库中找到id为" + treeNode.getId() +
				// "的哥哥节点");
			}
			// 上移操作影响的起始左值
			int leftValue = brother.getLeftValue();
			// 上移操作影响的结束右值
			int rightValue = treeNode.getRightValue();
			// 计算上移节点需要上移的数量
			int upNum = brother.getRightValue() - brother.getLeftValue() + 1;
			// 计算哥哥需要下移的数量
			int downNum = treeNode.getRightValue() - treeNode.getLeftValue()
					+ 1;

			// 更新上移节点的左右值,并标识所操作的数据,用来区分重复的左右值
			String updHqlRgt = "UPDATE " + clsTreeNode.getName()
					+ " SET leftValue = leftValue-" + upNum
					+ ",rightValue=rightValue-" + upNum
					+ ",strutSign='1' WHERE leftValue >= "
					+ treeNode.getLeftValue() + " and rightValue<="
					+ treeNode.getRightValue() + "and isolatedFlag = '"
					+ brother.getIsolatedFlag() + "'";
			// 更新哥哥节点的左右值
			String updHqlLft = "UPDATE " + clsTreeNode.getName()
					+ " SET leftValue = leftValue+" + downNum
					+ ",rightValue=rightValue+" + downNum
					+ " WHERE leftValue >=" + brother.getLeftValue()
					+ " and rightValue<=" + brother.getRightValue()
					+ " and isolatedFlag = '" + brother.getIsolatedFlag()
					+ "' and strutSign is null ";

			// 标识归位
			String updHqlStrutSign = "update "
					+ clsTreeNode.getName()
					+ " set strutSign='' where "
					+ "leftValue>=leftValue and rightValue<=rightValue and strutSign='1' and isolatedFlag='"
					+ brother.getIsolatedFlag() + "'";
			this.getSession().createQuery(updHqlRgt).executeUpdate();
			this.getSession().createQuery(updHqlLft).executeUpdate();
			this.getSession().createQuery(updHqlStrutSign).executeUpdate();
			//tx.commit();
		} catch (Exception e) {
			//tx.rollback();
			throw this.handleDaoException(e);
		}
	}

	/**
	 *
	 * <br/>方法描述:某个节点在本层节点间下移
	 *
	 * @param strutId
	 *            下移的节点id
	 * @throws DaoException
	 */
	public void moveDown(AbstractNestedSetTreeNode treeNode)
			throws DaoException {
		//Transaction tx = null;
		//tx = this.getSession().beginTransaction();
		try {
			Class clsTreeNode = treeNode.getClass();
			log.debug("BaseDao 开始上移一个 treeNode ");

			this.getSession().createQuery(
					"from " + clsTreeNode.getName() + " treeO").setLockMode(
					"treeO", LockMode.UPGRADE);

			// 获取哥哥节点
			AbstractNestedSetTreeNode brother = null;

			String queryBrother = " from " + clsTreeNode.getName()
					+ " where leftValue=" + (treeNode.getRightValue() + 1)
					+ " and isolatedFlag='" + treeNode.getIsolatedFlag() + "'";
			List list = this.getSession().createQuery(queryBrother).list();
			if (list.size() > 0) {
				brother = (AbstractNestedSetTreeNode) list.get(0);
			}
			if (brother == null) {
				return;
				// throw new DaoException("不能在数据库中找到id为" + treeNode.getId() +
				// "的哥哥节点");
			}
			// 上移操作影响的起始左值
			int leftValue = treeNode.getLeftValue();
			// 上移操作影响的结束右值
			int rightValue = brother.getRightValue();
			// 计算下移节点需要下移的数量
			int downNum = brother.getRightValue() - brother.getLeftValue() + 1;
			// 计算弟弟需要上移移的数量
			int upNum = treeNode.getRightValue() - treeNode.getLeftValue() + 1;

			// 更新下移节点的左右值,并标识所操作的数据,用来区分重复的左右值
			String updHqlRgt = "UPDATE " + clsTreeNode.getName()
					+ " SET leftValue = leftValue+" + downNum
					+ ",rightValue=rightValue+" + downNum
					+ ",strutSign='1' WHERE leftValue >= "
					+ treeNode.getLeftValue() + " and rightValue<="
					+ treeNode.getRightValue() + "and isolatedFlag = '"
					+ brother.getIsolatedFlag() + "'";
			// 更新哥哥节点的左右值
			String updHqlLft = "UPDATE " + clsTreeNode.getName()
					+ " SET leftValue = leftValue-" + upNum
					+ ",rightValue=rightValue-" + upNum + " WHERE leftValue >="
					+ brother.getLeftValue() + " and rightValue<="
					+ brother.getRightValue() + " and isolatedFlag = '"
					+ brother.getIsolatedFlag() + "' and strutSign is null ";

			// 标识归位
			String updHqlStrutSign = "update "
					+ clsTreeNode.getName()
					+ " set strutSign='' where "
					+ "leftValue>=leftValue and rightValue<=rightValue and strutSign='1'"
					+ " and isolatedFlag='" + brother.getIsolatedFlag() + "'";
			this.getSession().createQuery(updHqlRgt).executeUpdate();
			this.getSession().createQuery(updHqlLft).executeUpdate();
			this.getSession().createQuery(updHqlStrutSign).executeUpdate();
			//tx.commit();
		} catch (Exception e) {
			//tx.rollback();
			throw this.handleDaoException(e);
		}
	}

	/**
	 * 依据ID删除一个嵌套集合树节点
	 *
	 * @param treeNodeClazz 嵌套集合树节点Class
	 * @param oId 节点ID
	 * @throws DaoException
	 */
	public void delTreeNodeById(Class treeNodeClazz, Serializable oId)
			throws DaoException {
		//Transaction tx = null;
		//tx = this.getSession().beginTransaction();
		try {
			log.debug("BaseDao 开始删除一个 treeNode ");
			this.getSession().createQuery(
					"from " + treeNodeClazz.getName() + " treeO").setLockMode(
					"treeO", LockMode.UPGRADE);
			AbstractNestedSetTreeNode theNode = (AbstractNestedSetTreeNode) this
					.getSession().load(treeNodeClazz, oId);
			if (theNode != null) {
				int myLft = theNode.getLeftValue();
				int myRgt = theNode.getRightValue();
				int myWidth = myRgt - myLft + 1;
				String delHql = "DELETE " + treeNodeClazz.getName()
						+ " WHERE leftValue BETWEEN " + myLft + " AND " + myRgt
						+ " and isolatedFlag='" + theNode.getIsolatedFlag()
						+ "'";

				String updHqlLft = "UPDATE " + treeNodeClazz.getName()
						+ " SET leftValue = leftValue - " + myWidth
						+ " WHERE leftValue > " + myRgt + " and isolatedFlag='"
						+ theNode.getIsolatedFlag() + "'";

				String updHqlRgt = "UPDATE " + treeNodeClazz.getName()
						+ " SET rightValue = rightValue - " + myWidth
						+ " WHERE rightValue > " + myRgt
						+ " and isolatedFlag='" + theNode.getIsolatedFlag()
						+ "'";

				this.getSession().createQuery(delHql).executeUpdate();
				this.getSession().createQuery(updHqlLft).executeUpdate();
				this.getSession().createQuery(updHqlRgt).executeUpdate();
			}
			//tx.commit();
		} catch (Exception e) {
			//tx.rollback();
			throw this.handleDaoException(e);
		}
	}
	
	
	/**
	 * 
	 * <br/>方法描述:把某节点及其子孙节点复制到另一个节点下
	 * @param treeNodeClazz 嵌套集合树节点Class
	 * @param origTreeNode 需要复制的节点对象
	 * @param destTreeNode 复制目的节点对象
	 * @throws DaoException
	 */
	public void copyTreeNode(IDomainProvider domainProvider,Class treeNodeClazz,AbstractNestedSetTreeNode origTreeNode,AbstractNestedSetTreeNode destTreeNode)throws DaoException{
		//Transaction tx = null;
		//事务开始
		//tx = this.getSession().beginTransaction();
		try {
			log.debug("BaseDao 开始复制一个 treeNode ");
			
			this.getSession().createQuery(
					"from " + treeNodeClazz.getName() + " treeO").setLockMode(
					"treeO", LockMode.UPGRADE);
			//复制后需要后移的节点开始值【复制目的节点右值往后的节点都需要后移】
			int moveStartValue = destTreeNode.getRightValue();
			//需要移动的左右值总数=复制节点右值-左值+1
			int moveValue = origTreeNode.getRightValue()-origTreeNode.getLeftValue()+1;
			//复制节点新左右值和原来左右值的差量
			int changeValue = origTreeNode.getLeftValue()-destTreeNode.getRightValue();
			//复制节点新层次和原来左右值的差量
			int changTreeLevel = origTreeNode.getTreeLevel()-destTreeNode.getTreeLevel()-1;
			
			//计算在操作中被做了标记的左右值范围
			int leftValue = origTreeNode.getLeftValue()-changeValue;
			int rightValue = origTreeNode.getRightValue()-changeValue;
			
			String updHqlLft = "UPDATE " + treeNodeClazz.getName()+ " SET leftValue = leftValue + " + moveValue
					+ " WHERE leftValue >= " + moveStartValue + " and isolatedFlag='"+ origTreeNode.getIsolatedFlag() + 
					"' and strutSign is null";

			String updHqlRgt = "UPDATE " + treeNodeClazz.getName()+ " SET rightValue = rightValue + " + moveValue
					+ " WHERE rightValue >= " + moveStartValue+ " and isolatedFlag='" + origTreeNode.getIsolatedFlag()+ 
					"' and strutSign is null";
			
			String upHsqlStrutSign = "update " + treeNodeClazz.getName()+ " set strutSign='' where " +
					"leftValue>="+leftValue+" and rightValue<="+rightValue+" and isolatedFlag='"+origTreeNode.getIsolatedFlag()+"'";
			this.getSession().createQuery(updHqlLft).executeUpdate();
			this.getSession().createQuery(updHqlRgt).executeUpdate();
			this.getSession().createQuery(upHsqlStrutSign).executeUpdate();
			//tx.commit();
		} catch (Exception e) {
			//tx.rollback();
			throw this.handleDaoException(e);
		}
	}
	
	/**
	 * 
	 * <br/>方法描述:把某节点及其子孙节点移动到另一个节点下
	 * @param origTreeNodeId 被移动节点id
	 * @param destTreeNodeId 移动目的节点id
	 * @throws DaoException
	 */
	public void moveTreeNode(IDomainProvider domainProvider,Class treeNodeClazz,AbstractNestedSetTreeNode origTreeNode,AbstractNestedSetTreeNode destTreeNode)throws DaoException{
		//Transaction tx = null;
		//事务开始
		//tx = this.getSession().beginTransaction();
		try {
			log.debug("BaseDao 开始复制一个 treeNode ");
			this.getSession().createQuery(
					"from " + treeNodeClazz.getName() + " treeO").setLockMode(
					"treeO", LockMode.UPGRADE);
			//需要移动的左右值总数=复制节点右值-左值+1
			int moveValue = origTreeNode.getRightValue()-origTreeNode.getLeftValue()+1;
			//复制节点新左右值和原来左右值的差量
			int changeValue = origTreeNode.getLeftValue()-destTreeNode.getRightValue();
			
			//计算在操作中被做了标记的左右值范围
			int leftValue = origTreeNode.getLeftValue()-changeValue;
			int rightValue = origTreeNode.getRightValue()-changeValue;
			
			//记录是否向前移动,true:向前;false:向后
			boolean front = true;
			if(origTreeNode.getLeftValue()<destTreeNode.getLeftValue()){
				front = false;
			}
			
			//删除需要移动的节点
			String deleteHql = "delete from " + treeNodeClazz.getName()+ " where leftValue>="+origTreeNode.getLeftValue()
					+" and rightValue<="+origTreeNode.getRightValue()+" and isolatedFlag='"+origTreeNode.getIsolatedFlag()
					+"' and strutSign is null";
			String updHqlLft = "";
			String updHqlRgt = "";
			String upHsqlStrutSign = "";
			if(front){
				//向前移动
				//修改移动左右值>=移动目的对象的左值 and<被移动节点的左值且没有做标识的节点
				updHqlLft = "UPDATE " + treeNodeClazz.getName()+ " SET leftValue = leftValue + " + moveValue
					+ " WHERE leftValue >= " + destTreeNode.getRightValue() + " and leftValue<"+
					origTreeNode.getLeftValue()+" and isolatedFlag='"+ origTreeNode.getIsolatedFlag() + 
					"' and strutSign is null";

				updHqlRgt = "UPDATE " + treeNodeClazz.getName()+ " SET rightValue = rightValue + " + moveValue
					+ " WHERE rightValue >= " + destTreeNode.getRightValue()+ " and rightValue<"+
					origTreeNode.getLeftValue()+" and isolatedFlag='" + origTreeNode.getIsolatedFlag()+ 
					"' and strutSign is null";
				upHsqlStrutSign = "update " + treeNodeClazz.getName()+ " set strutSign='' where " +
						"leftValue>="+leftValue+" and rightValue<="+rightValue +" and strutSign='1' and " +
						"isolatedFlag='" + origTreeNode.getIsolatedFlag()+"'";
			}else{
				//向后移动
				//修改移动左右值>被移动节点的右值 and<移动目的节点的右值且没有做标识的节点
				updHqlLft = "UPDATE " + treeNodeClazz.getName()+ " SET leftValue = leftValue - " + moveValue
					+ " WHERE leftValue > " + origTreeNode.getRightValue() + " and leftValue<"+
					destTreeNode.getRightValue()+" and isolatedFlag='"+ origTreeNode.getIsolatedFlag() + 
					"' and strutSign is null";

				updHqlRgt = "UPDATE " + treeNodeClazz.getName()+ " SET rightValue = rightValue - " + moveValue
					+ " WHERE rightValue > " + origTreeNode.getRightValue() + " and rightValue<"+
					destTreeNode.getRightValue()+" and isolatedFlag='" + origTreeNode.getIsolatedFlag()+ 
					"' and strutSign is null";
				
				upHsqlStrutSign = "update " + treeNodeClazz.getName()+ " set strutSign='' " +
						",leftValue=leftValue-"+moveValue+" ,rightValue=rightValue-"+moveValue+" where " +
						"leftValue>="+leftValue+" and rightValue<="+rightValue +" and strutSign='1' and " +
						"isolatedFlag='" + origTreeNode.getIsolatedFlag()+"'";
			}
			this.getSession().createQuery(deleteHql).executeUpdate();
			this.getSession().createQuery(updHqlLft).executeUpdate();
			this.getSession().createQuery(updHqlRgt).executeUpdate();
			this.getSession().createQuery(upHsqlStrutSign).executeUpdate();
			//tx.commit();
		} catch (Exception e) {
			//tx.rollback();
			throw this.handleDaoException(e);
		}
	}

}
